#include <gtk/gtk.h>
#include <glade/glade-xml.h>
#include <unistd.h>
#include <gdk/gdkkeysyms.h>
#include <libmpd/libmpd.h>

#include "plist.h"
#include "library.h"

//embedded ui.xml
extern char ui[];
extern guint ui_length;

static GtkWidget *window;
static Library *library;
static GtkLabel *status;
static GtkLabel *artist;
static GtkLabel *title;
static gboolean progress_dragging = FALSE;
static GtkRange *progress;
static GtkTreeView *playlist;
static GtkScrolledWindow *playlist_sw;
static MpdObj *mpd;

static void
player_prev()
{
	int elapsed = 0;
	int state = mpd_player_get_state(mpd);
	if(state == MPD_STATUS_STATE_PAUSE || state == MPD_STATUS_STATE_PLAY){
		elapsed = mpd_status_get_elapsed_song_time(mpd);
		if(elapsed > 9){
			mpd_player_stop(mpd);
			mpd_player_play(mpd);
			mpd_player_prev(mpd);
		}
		else
			mpd_player_prev(mpd);
	}
}

static void 
player_play()
{
	if(mpd_player_get_state(mpd) == MPD_STATUS_STATE_PLAY)
		mpd_player_stop(mpd);
	mpd_player_play(mpd);
	gtk_widget_queue_draw(GTK_WIDGET(playlist));
}

static void
player_pause()
{
	mpd_player_pause(mpd);
}

static void
player_stop()
{
	mpd_player_stop(mpd);
}

static void
player_next()
{
	mpd_player_next(mpd);
}

static char* 
progress_format(GtkScale *scale, gdouble d_value, gpointer user_data)
{
	int value = d_value;
	return g_strdup_printf("%d:%02d", value / 60, value%60);
}

static gboolean
progress_change_value()
{
	progress_dragging = TRUE;
	return FALSE;
}

static gboolean
progress_value_changed(GtkRange *range, gpointer data)
{
	if(!progress_dragging)
		return FALSE;

	mpd_player_seek(mpd, gtk_range_get_value(GTK_RANGE(range)));
	progress_dragging = FALSE;
	
	return TRUE;
}

static void
progress_update()
{
	if(progress_dragging)
		return;

	int state = mpd_player_get_state(mpd);

	if(state == MPD_STATUS_STATE_PAUSE || state == MPD_STATUS_STATE_PLAY){
		g_object_set(progress, "sensitive", TRUE, NULL);
		int elapsed = mpd_status_get_elapsed_song_time(mpd);
		int total = mpd_status_get_total_song_time(mpd);
		gtk_range_set_range(progress, 0, total);
		gtk_range_set_value(progress, elapsed);
	}else{
		g_object_set(progress, "sensitive", FALSE, NULL);
	}
}

static void
mpd_status_changed(MpdObj * mi, ChangedStatusType what, void *userdata)
{
	//playlist changed
	if(what & (MPD_CST_PLAYLIST)){
		TreeState *state = library_save_tree_state(playlist);
		GtkTreeModel *model = GTK_TREE_MODEL(plist_new(mpd));
		gtk_tree_view_set_model(playlist, model);
		g_object_unref(model);
		library_restore_tree_state(playlist, state);
	}
	
	//progress changed
	if (what & (MPD_CST_ELAPSED_TIME|MPD_CST_TOTAL_TIME|MPD_CST_STATE)){
		progress_update();
	}

	//update title & artist
	if(what & MPD_CST_SONGID){
		mpd_Song *song = mpd_playlist_get_current_song(mpd);
		if(song && artist && title){
			gtk_label_set_text(artist, song->artist);
			gtk_label_set_text(title, song->title);
		}
		gtk_widget_queue_draw(GTK_WIDGET(playlist));
	}

	if(what & MPD_CST_DATABASE){
		library_db_changed(library);
	}
	
	if(what & MPD_CST_UPDATING){
		static gboolean updating = FALSE;
		updating = !updating;
		if(updating)
			gtk_label_set_text(status, "Updating...");
		else
			what |= MPD_CST_PLAYLIST;
	}

	//update_statusbar
	if(what & MPD_CST_PLAYLIST){
		int total = 0;
		int length = mpd_playlist_get_playlist_length(mpd);
		for(int i=0; i<length; i++){
			mpd_Song *song = mpd_playlist_get_song_from_pos(mpd, i);
			if(song)
				total += song->time;
			mpd_freeSong(song);
		}
		char *report = g_strdup_printf("%d Items, Duration: %d min", length, total/60);
		gtk_label_set_text(status, report);
		g_free(report);
	}
	
}

static void
mpd_error(MpdObj * mi, int id, char *msg, void *userdata)
{
	if(id == 50)
		return;

	char *utf_msg = g_locale_to_utf8(msg, -1, NULL, NULL, NULL);
	GtkWidget* dialog = gtk_message_dialog_new (GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,
												GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
												"Error(%d): %s\n", id, utf_msg);
	gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
	g_free(utf_msg);	
}

static gboolean 
mpd_status_poll()
{
	mpd_status_update(mpd);
	return TRUE;
}

static void
playlist_row_activated(GtkTreeView *tree_view, GtkTreePath *path, 
					   GtkTreeViewColumn *column, gpointer user_data)
{
	GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
	GtkTreeIter iter;
	if(!gtk_tree_model_get_iter(model, &iter, path))
		return;
	mpd_player_play_id(mpd, plist_get_song_id(PLIST(model), &iter));
}

static void
playlist_add()
{
	int width, height, x, y;
	gtk_window_get_size(GTK_WINDOW(window), &width, &height);
	gtk_window_get_position(GTK_WINDOW(window), &x, &y);
	gtk_window_move(GTK_WINDOW(library), x+width+10, y);
	gtk_window_resize(GTK_WINDOW(library), 1, height);

	gtk_widget_show_all(GTK_WIDGET(library));
}


static void
playlist_clear()
{
	mpd_playlist_clear(mpd);
}

static void
playlist_updatedb()
{
	mpd_database_update_dir(mpd, "/");
}

static void
playlist_remove()
{
	GtkTreeModel *model = gtk_tree_view_get_model(playlist);
	GtkTreeSelection *selection = gtk_tree_view_get_selection(playlist);
	GList *paths = gtk_tree_selection_get_selected_rows(selection, NULL);
	GList *ids = NULL;
	for(GList *it = paths;it;it = it->next){
		GtkTreeIter iter;
		gtk_tree_model_get_iter(model, &iter, it->data);
		int songid = plist_get_song_id(PLIST(model), &iter);
		ids = g_list_append(ids, (gpointer)songid);
	}
	g_list_foreach (paths, (GFunc)gtk_tree_path_free, NULL);
	g_list_free (paths);
	for(GList *it = ids;it;it = it->next)
		mpd_playlist_queue_delete_id(mpd, (int)it->data);
	mpd_playlist_queue_commit(mpd);
	g_list_free(ids);
}

static gboolean
topwindow_key(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
	struct {
		guint keyval;
		void (*handler)();
	} key_map[] = {
		{GDK_z, player_prev},
		{GDK_x, player_play},
		{GDK_c, player_pause},
		{GDK_v, player_stop},
		{GDK_b, player_next},
		{GDK_Delete, playlist_remove},
	};
	for(int i=0; i<G_N_ELEMENTS(key_map); i++){
		if(event->keyval == key_map[i].keyval)
			key_map[i].handler();
	}

	return FALSE;
}

static void
init_signals(GladeXML *xml)
{
#define signal(x) {#x, (GCallback) x }
	struct {
		char *name;
		GCallback handler;
	}signal_table[]={
		signal(gtk_main_quit),
		signal(gtk_widget_hide_on_delete),
		signal(player_play),
		signal(player_prev),
		signal(player_next),
		signal(player_stop),
		signal(player_pause),
		signal(progress_format),
		signal(progress_change_value),
		signal(progress_value_changed),
		signal(playlist_row_activated),
		signal(playlist_remove),
		signal(playlist_add),
		signal(playlist_clear),
		signal(playlist_updatedb),
		signal(topwindow_key),
	};
#undef signal
	for(int i=0; i<G_N_ELEMENTS(signal_table); i++){
		glade_xml_signal_connect (xml, signal_table[i].name, signal_table[i].handler);
	}
}

static void
playlist_init(GtkTreeView *tv)
{
	struct {
		char *name;
		int colid;
		gboolean expand;
	} columns [] = {
		{"Title", PLIST_COLUMN_TITLE, TRUE},
		{"Length", PLIST_COLUMN_LENGTH, FALSE},
	};

	GtkTargetEntry can_send[] = {
		{"splayer-playlist-song", GTK_TARGET_SAME_APP|GTK_TARGET_SAME_WIDGET, 0},
	};

	GtkTargetEntry can_receive[] = {
		{"splayer-playlist-song", GTK_TARGET_SAME_APP|GTK_TARGET_SAME_WIDGET, 0},
		{"splayer-db-path", GTK_TARGET_SAME_APP, 0},
	};

	gtk_tree_view_enable_model_drag_source(tv, GDK_BUTTON1_MASK, 
										   can_send, G_N_ELEMENTS(can_send),
										   GDK_ACTION_MOVE);
	gtk_tree_view_enable_model_drag_dest(tv, can_receive, G_N_ELEMENTS(can_receive),
										 GDK_ACTION_MOVE|GDK_ACTION_COPY);

	g_object_set(tv, "rules-hint", TRUE, NULL);
	for(int i=0; i<G_N_ELEMENTS(columns); i++){
		GtkCellRenderer *cr = gtk_cell_renderer_text_new();
		g_object_set(cr, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
		GtkTreeViewColumn *column = 
			gtk_tree_view_column_new_with_attributes(columns[i].name, cr, 
													 "text", columns[i].colid,
													 "weight", PLIST_COLUMN_WEIGHT,
													 NULL);
		gtk_tree_view_column_set_expand(column, columns[i].expand);
		gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
		gtk_tree_view_append_column (tv, column);
	}

	GtkTreeSelection *selection = gtk_tree_view_get_selection(tv);
	gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
}

void
_disable_focus(GtkWidget *container)
{
	GList *items = gtk_container_get_children(GTK_CONTAINER(container));
	for(;items;items = items->next){
		GtkWidget *el = GTK_WIDGET(items->data);
		GTK_WIDGET_UNSET_FLAGS(el, GTK_CAN_FOCUS|GTK_HAS_FOCUS);
	}
}

int
main(int argc, char* argv[])
{
	gtk_init(&argc, &argv);

	// load ui
	GladeXML *xml = glade_xml_new_from_buffer(ui, ui_length-1, "topwindow", NULL);

	//signals
	init_signals(xml);

	//get some widgets
	window = glade_xml_get_widget(xml, "topwindow");
	progress = GTK_RANGE(glade_xml_get_widget(xml, "progress"));
	artist = GTK_LABEL(glade_xml_get_widget(xml, "artist"));
	title = GTK_LABEL(glade_xml_get_widget(xml, "title"));
	status = GTK_LABEL(glade_xml_get_widget(xml, "status"));
	playlist_sw = GTK_SCROLLED_WINDOW(glade_xml_get_widget(xml, "playlist-sw"));

	//turn off canfocus
	_disable_focus(glade_xml_get_widget(xml, "buttonbox"));
	_disable_focus(glade_xml_get_widget(xml, "toolbox"));
	
	//set font weight of title to 800
	PangoAttrList *list = pango_attr_list_new();
	PangoAttribute *attr = pango_attr_weight_new(PANGO_WEIGHT_ULTRABOLD);
	attr->start_index = 0;
	attr->end_index = G_MAXINT;
	pango_attr_list_insert(list, attr);
	gtk_label_set_attributes(title, list);
	pango_attr_list_unref(list);

	// connect to mpd
	mpd = mpd_new_default();
	mpd_signal_connect_error(mpd, mpd_error, NULL);
	mpd_signal_connect_status_changed(mpd, mpd_status_changed, NULL);
	if(mpd_connect(mpd))
		return 1;

	//get & init playlist
	playlist = GTK_TREE_VIEW(glade_xml_get_widget(xml, "playlist"));
	playlist_init(playlist);

	//get & init library dialog
	library = library_new(mpd, GTK_WINDOW(window));
	
	//start polling function
	g_timeout_add(100, (GSourceFunc)mpd_status_poll, mpd);

	//show window
	gtk_widget_show_all(window);

	gtk_main();

	mpd_free(mpd);

	return 0;
}
